'use strict';

var app = require('../../server/server');
var moment = require('moment')

moment.locale('zh-cn')

module.exports = function(Match) {

  Match.disableRemoteMethod('__count__games', false);
  // Match.disableRemoteMethod('__create__games', false);
  Match.disableRemoteMethod('__delete__games', false);
  Match.disableRemoteMethod('__destroyById__games', false);
  Match.disableRemoteMethod('__findById__games', false);
  // Match.disableRemoteMethod('__get__games', false);
  Match.disableRemoteMethod('__updateById__games', false);

  Match.disableRemoteMethod('__count__players', false);
  Match.disableRemoteMethod('__create__players', false);
  Match.disableRemoteMethod('__delete__players', false);
  Match.disableRemoteMethod('__destroyById__players', false);
  Match.disableRemoteMethod('__findById__players', false);
  Match.disableRemoteMethod('__get__players', false);
  Match.disableRemoteMethod('__updateById__players', false);

   Match.disableRemoteMethod('__exists__users', false);
   Match.disableRemoteMethod('__upsert__users', false);
   Match.disableRemoteMethod('__count__users', false);
   Match.disableRemoteMethod('__delete__users', false);
   Match.disableRemoteMethod('__create__users', false);
   Match.disableRemoteMethod('__update__users', false);
   Match.disableRemoteMethod('__get__users', false);
   Match.disableRemoteMethod('__findById__users', false);
   Match.disableRemoteMethod('__updateById__users', false);
   Match.disableRemoteMethod('__destroyById__users', false);
   Match.disableRemoteMethod('__updateAll__users', false);
   Match.disableRemoteMethod('__link__users', false);
   Match.disableRemoteMethod('__unlink__users', false);


   Match.observe('loaded', function(ctx, next) {
    if (ctx.data && !ctx.data.startDate) {
      ctx.data.startDate = moment(ctx.data.startedAt).valueOf()
    }

    next()
   })

   Match.observe('before save', function(ctx, next) {
    if (ctx.instance) {
      ctx.instance.unsetAttribute('startDate')
    } else {
      delete ctx.data['startDate']
    }

    next()
   })

  function canStartGame(match) {
    return match.isSeted && match.userModelIds.length === 4
  }

  // Note:
  // Timing emit is not always right
  // issue 1: first settings, final join user.
  /*Match.observe('after save', function(ctx, next) {
    // Read-only
    const match = ctx.instance
    if (canStartGame(match)) {
      match.games.create()
    }

    next()
  })*/

  // Tow steps:
  // wechat user already store in database.
  // 1) First join the room (required [id, matchCode, openid, headimgurl, nickname] )
  // 2) Only First one participating the room can meke rules & base
  Match.wechatJoin = function (user, clientId, tableId, position) {
    const { id, openid, nickname, matchCode = '1234' } = user
    const filterParams = {
      where: {
        matchCode: matchCode,
        tableId: tableId,
        hasLeft: false,
        startedAt: { gt: Date.now() - 4 * 60 * 60 * 1000 }    // 距现在 4 小时以内的 Match
      },
      include: 'users'
    }

    console.log('userid', id)

    return Match
      .findOrCreate(filterParams, { matchCode, tableId })
      .then(([ match, isNewInstance ]) => {
        isNewInstance = isNewInstance || !match.userModelIds.length
        const hasHost = !!match._players.find(player => player.isHost)
        const existingPlayer = !isNewInstance && match.__data.users.find(player => String(player.id) === String(user.id))
        const isFull = match._players.length >= 4

        app.pushDataToClient('me', clientId, Object.assign({
          position,
          isHost: existingPlayer ? existingPlayer.isHost : !hasHost
        }, user))

        isNewInstance && console.log('======= 新建房间 =======')

        if (isFull) {
          console.log('==== 已坐满 4 人，不能再加 ===== ')
          return Promise.reject({ error: '已坐满 4 人，不能再加' })
        }

        if (existingPlayer) {
          console.log('==== 用户已加入 ===== ')
          // 换位置
          const clientIds = app.getClientIdsFromUser(tableId, user).filter(_clientId => _clientId !== clientId)
          console.log(clientIds)
          app.broadcastToSome('matchChangeSeat', clientIds, {})
          app.pushDataToClient('match', clientId, match.__data)

          // Make sure same user related to one client
          app.unbindSocket(user)
          app.bindSocket(clientId, Object.assign({ isHost: existingPlayer.isHost, position }, user), match.id)
          // app.broadcastToSome('match', app.getClientsFromTable(tableId), match)
          return match
        }

        return addPlayer(match, user, position, !hasHost)
          .then( user => {
            console.log('==== 用户 ===')
            console.log(user)
            return Match.findOne(filterParams)
          })
          .then(match => {
            const pushData = Object.assign({}, match.__data)
            if (match._players.length > 1) {
              console.log('======= 加入房间 =======')
              pushData.newPlayer = {
                id: user.id,
                name: user.nickname,
                avatar: user.avatar,
                headimgurl: user.headimgurl,
                position: position
              }
            }
            app.bindSocket(clientId, Object.assign({ isHost: !hasHost, position }, user), match.id)
            app.broadcastToSome('match', app.getClientsFromTable(tableId), pushData)

            if (canStartGame(match)) {
              match.games.create()
            }

            return match
          })
      })
  }

  function addPlayer(match, user, position, isHost = false) {
    return match.users
      .add(user.id)
      .then(user => {
        return match.players.create({
          id: user.id,
          name: user.nickname,
          userid: user.id,
          position: position,
          isHost: isHost,
          avatar: user.avatar
        })
      })
  }

  function removePlayer(match, userid) {
    return match.users.remove(userid)
      .then(user => {
        console.log('==== match.users.remove ==== ')
        console.log(user)
        match.players.destroy(userid, (err, _user) => {
          console.log('==== match.players.destroy ==== ')
          err && console.log(err)
          console.log(_user)
          err && Promise.reject(err)
          return user
        })
      })
  }

  // 规则制定
  Match.prototype.createSettings = function(req, res) {
    const { matchId, matchCode, rules, optionalPatterns, base, ceiling } = req
    const alreadySet = this.isSeted === true
    if (alreadySet) {
      res.status(412).send('The Rules have been already made.')
      return
    }

    this.base = base
    this.ceiling = ceiling
    this.rules = rules
    this.optionalPatterns = optionalPatterns
    this.isSeted = true

    this.save()
      .then(match => {
        app.broadcastToSome('match', app.getClientsFromTable(match.tableId), match)
        if (canStartGame(match)) {
          match.games.create()
        }

        res.send(match)
      })
      .catch(err => res.send(err))
  }

  Match.remoteMethod(
    'prototype.createSettings', {
      accepts: [
        { arg: 'data', type: 'object', http: { source: 'body' } },
        { arg: 'res', type: 'object', http: { source: 'res' } }
      ]
    }
  );


  // 用户填名字进入
  Match.userJoin = function (req, res) {
    const { name, clientId, tableId, position } = req
    const UserModel = app.models.UserModel

    UserModel
      .create({ nickname: name })
      .then(user => {
        const bindUserData = Object.assign({}, user.__data, { position })
        console.log('user.__data', bindUserData)
        app.bindSocket(clientId, bindUserData)
        // app.pushDataToClient('me', clientId, bindUserData)
        // return Match.wechatJoin(Object.assign({ clientId }, user.__data ))
        return Match.wechatJoin(user.__data, clientId, tableId, position)
      })
      .then(match => res.send(match))
      .catch(err => {
        console.log(err)
        res.send(err)
      })
  }

  Match.remoteMethod(
    'userJoin', {
      accepts: [
        { arg: 'data', type: 'object', http: { source: 'body' } },
        { arg: 'res', type: 'object', http: { source: 'res' } }
      ]
    }
  );

  Match.join = function (req, res) {
    var matchCode = req.matchCode;
    var playerName = req.playerName;
    Match.findOrCreate({
      where: {
        matchCode: matchCode,
        startedAt: { gt: Date.now() - 4 * 60 * 60 * 1000 }    // 距现在 4 小时以内的 Match
      }
    }, {
        matchCode: matchCode,
        base: req.base,
        rules: req.rules
    }, function (err, match) {
      if (err) return;
      var existingPlayer = match._players.find( player => player.name == playerName );
      if (!existingPlayer) {
        if (match.players.length >= 4) {
          res.status(412).send({ error: '已坐满 4 人，不能再加' });
          return;
        }
        match.players.create({ name: playerName }, function (err, player) {
          match['me'] = player
          res.json(match)
          app.pushData(match.id, 'match', match);

          if (match._players.length == 4) {
            match.games.create({});
          }
        });
      } else {
        match['me'] = existingPlayer;
        res.json(match);
      }
    });
  };

  Match.remoteMethod(
    'join', {
      accepts: [
        { arg: 'data', type: 'object', http: { source: 'body' } },
        { arg: 'res', type: 'object', http: { source: 'res' } }
      ]
    }
  );

  Match.prototype.leave = function(userid, res) {
    console.log('======= prototype.leave  ========')

    const player = this._players.find(player => String(player.userid) === String(userid))
    if (!player) {
      console.log('====== 用户不存在 =========')
      res.status(412).send({ error: '用户不存在'})
      return
    }

    console.log('====== 退出房间 =========')
    console.log(player)

    if (!canStartGame(this)) {

      if (player.isHost) {
        this.rules = []
        this.base = 2
        this.ceiling = 4
        this.isSeted = false
      }

      this.save()
        .then( match => {
          removePlayer(this, userid)
            .then(user => {
              console.log('===用户离开====')
              app.unbindSocket(player)
              app.broadcastToSome('matchBack', app.getClientsFromTable(match.tableId), {
                player: player,
                match: match
              })
              res.send(user || {'text': '删除用户'})
            })
            .catch(error => res.send({ error }))
        })

      return
    }

    if (!this.hasLeft) {
      this.hasLeft = true
      this.endDate = new Date()
    }

    this.save()
      .then( match => {
        app.unbindSocket(player)
        this.tableId && app.broadcastToSome('matchLeave', app.getClientsFromTable(this.tableId), {
          player: player,
          match: match
        })

        match._players.forEach(player => app.unbindSocket(player))
        res.send(player)
      })
      .catch(error => {
        console.log(error)
        res.status(412).send({ error })
      })
  }
  Match.remoteMethod(
    'prototype.leave', {
      description: '用户退出Match',
      accepts: [
        { arg: 'userid', type: 'string', required: true },
        { arg: 'res', type: 'object', http: { source: 'res' } }
      ],
      http: { path: '/users/:userid/leave', verb: 'post' }
    }
  );
  Match.findByUserid = function(userid, include = ['users', 'games']) {
    return Match
      .find({
        where: {
          userModelIds: { inq: [ userid ] }
        },
        include: include
      })
  }

  Match.findByPlayerId = function(userid, type, include = ['users', 'games']) {
    const whereParams = {
       _players: {
         elemMatch: { id: String(userid) }
      }
    }

    if (type === '血流成河') {
      whereParams.rules = { inq: [ '血流成河' ] }
    } else if (type === '血战到底') {
      whereParams.rules = { nin: [ '血流成河' ] }
    } else if (type === 'all') {

    }

    return Match.find({
        where: whereParams,
        include: include,
        order: 'startedAt DESC'
      })
  }

  Match.getBoard = function (user, type = 'xlch') {
    type = type === 'xlch' ? '血流成河' : '血战到底'
    let gameSum = 0
    let huSum = 0, paoSum = 0, tingSum = 0
    let huRatio = 0, paoRatio = 0, tingRatio = 0
    let maxTimes = 0

    // return Match
      // .findByUserid(user.id)
    return Match
      .findByPlayerId(user.id, type)
      .then(matches => {
        console.log(matches)
        matches.forEach(match => {
          console.log('=========== Math Board  ===============')
          console.log(type, match.__data.games.length)
          const result = statBoard(match, user)
          gameSum += match.__data.games.length
          huSum += result['huSum']
          paoSum += result['paoSum']
          tingSum += result['tingSum']
          maxTimes = Math.max(result.maxTimes, maxTimes)
        })

        console.log('gameSum', gameSum)
        console.log('huSum', huSum)
        console.log('paoSum', paoSum)
        console.log('tingSum', tingSum)
        console.log('maxTimes', maxTimes)

        huRatio = gameSum ? huSum / gameSum : 0
        paoRatio = gameSum ? paoSum / gameSum : 0
        tingRatio = gameSum ? tingSum / gameSum : 0

        return { huRatio, paoRatio, tingRatio, maxTimes }
      })
  }

  Match.calcWinRatio = function(user) {
    let gameSum = 0
    let winSum = 0
    return Match
      .findByPlayerId(user.id, 'all')
      .then(matches => {
        matches.forEach(match => {
          gameSum += match.__data.games.length
          match.__data.games.forEach(game => {
            game.results.forEach(result => {
              if (String(result.id) === String(user.id) && result.score > 0) {
                winSum += 1
              }
            })
          })
        })

        console.log("========= 总胜率 ===========")
        console.log('gameSum', gameSum)
        console.log('winSum', winSum)

        return gameSum ? winSum / gameSum : 0
      })
  }

  Match.statMatchesbByUser = function(user) {
    const outputs = []

    return Match
      .findByPlayerId(user.id, 'all')
      .then(matches => {
        matches.forEach(match => {
          const otherPlayers = match._players.filter(player => String(player.id) !== String(user.id))
          const isToday = moment(match.startedAt).isSame(Date.now(), 'day')
          let score = 0

          match.__data.games.forEach(game => {
            game.results.forEach(result => {
              if (String(result.id) === String(user.id)) {
                score += result.score
              }
            })
          })

          outputs.push({
            matchId: match.id,
            month: moment(match.startedAt).format('M'),
            startedAt: match.startedAt,
            date: {
              label: isToday ? '今天' : moment(match.startedAt).format('ddd'),
              time: moment(match.startedAt).format(isToday ? 'HH:mm' : 'MM-DD')
            },
            type: match.rules.includes('血流成河') ? 'xlch' : 'xzdd',
            players: otherPlayers,
            score: score,
          })
        })

        let out = {}
        outputs.forEach( el => {
          out[el.month] = (out[el.month] || []).concat(el)
        })
        out.months = Object.keys(out).sort().reverse()

        return out
      })
  }

  Match.statMatchByUser = function(matchId, user) {
    const outputs = []

    return Match
      .findOne({ where: { id: matchId }, include: ['games'] })
      .then(match => {

        match.__data.games.forEach(game => {
          let score = 0
          let actions = []

          console.log(game.results)

          game.results.forEach(result => {
            if (String(result.id) === String(user.id)) {
              score = result.score
            }
          })

          game._actions.forEach(action => {
            if (String(action.owner.id) === String(user.id)) {
              action.attributes.forEach( attr => {
                action.targets.forEach( target => {
                  actions.push(attr + target.name)
                })
              })
            } else if (!!action.targets.find(target => target.id === String(user.id))) {
              action.attributes.forEach( attr => {
                actions.push('被' + action.owner.name + attr)
              })
              }
          })

          outputs.push({
            date: game.startTime ? moment(game.startTime).format('HH:mm'): '',
            score: score,
            actions: actions
          })

        })


        return outputs
      })
  }

  Match.prototype.result = function (res) {
    console.log('=====> match result =======')
    const output = {}
    const players = Array.from(this._players)
    let maxScore // 总分最大分数
    let minScore // 总分最小分数
    let singleMaxScore // game最大分数

    Match.findOne({ where: { id: this.id }, include: ['games'] })
      .then(match => {

        output.gameCount = match.__data.games.length
        output.durationFormat = formatDuration(
          (this.endDate ? moment(this.endDate).valueOf() : moment().valueOf()) - moment(this.startedAt).valueOf()
        )

        match.__data.games.forEach( game => {

          players.forEach( player => {

            game.results.forEach( result => {
              singleMaxScore = singleMaxScore ? Math.max(singleMaxScore, result.score) : result.score

              if (player.id == result.id + '') {
                player.score = (player.score || 0) + result.score
                player.singleMaxScore = player.singleMaxScore ? Math.max(result.score, player.singleMaxScore) : result.score
                maxScore = maxScore ? Math.max(player.score, maxScore) : player.score

                game.results
                  .filter(el => el.id != player.id + '')
                  .forEach(el => {
                    player[`scorefrom_${el.name}_${el.id}`] = (player[`scorefrom_${el.name}_${el.id}`] || 0) + (result[`scorefrom_${el.name}_${el.id}`] || 0)
                  })
              }
            })


            game._actions.forEach( action => {
              if (action.owner.id == player.id + '') {
                if (action.type == '胡') {
                  player.huSum = (player.huSum || 0) + 1
                }

                if (action.type == '杠') {
                  player.gangSum = (player.gangSum || 0) + 1
                }

                if (action.type == '查叫') {
                  player.chajiaoSum = (player.chajiaoSum || 0) + 1
                }
              }
            })
          })

        })

        maxScore = Math.max.apply(null, players.map(el => el.score))
        minScore = Math.min.apply(null, players.map(el => el.score))

        players.forEach( player => {
          if (singleMaxScore === player.singleMaxScore) {
            player.singleBigWinner = true
          }

          if (maxScore == player.score) {
            player.bigWinner = true
          }

          if (minScore == player.score) {
            player.bigLoser = true
          }

          delete player.singleMaxScore
        })

        console.log('singleMaxScore', singleMaxScore)
        console.log('maxScore', maxScore)

        output.players = players.sort((a, b) => b.score - a.score)
        // res.send(output)
        res.render('personalHome/match-share', { match: output })
      })
      .catch(err => res.render('personalHome/match-share', { match: null }))

    /*Match.findOne({ where: { id: this.id }, include: ['games'] })
      .then(match => {

        output.gameCount = match.__data.games.length
        output.durationFormat = formatDuration(
          this.endDate ? moment(this.endDate).valueOf() : moment().valueOf() - moment(this.startedAt).valueOf()
        )
        match.__data.games.forEach( game => {
          game.results.forEach( result => {
            players.forEach( player => {
              if (String(player.id) === String(result.id)) {
                player.score = (player.score || 0) + result.score
              }
            })
          })
        })
        output.players = players
        // res.send(output)
        res.render('personalHome/match-result', { match: output})
      })
      .catch(err => res.render('personalHome/match-result', { match: null }))*/
  }
  Match.remoteMethod(
    'prototype.result',
    {
      description: "Return match result",
      accepts: [
        { arg: 'res', type: 'object', http: { source: 'res' } }
      ],
      http: { path: '/result', verb: 'get' }
    }
  )

};


function statBoard(match, user) {
  let huSum = 0
  let paoSum = 0
  let tingSum = 0
  let maxTimes = 0

  match.__data.games.forEach(game => {
    let hasHu = false
    let hasTing = false
    let beichajiaoAction = null
    game._actions.forEach( action => {
      // 胡牌
      if (action.type === '胡' && String(action.owner.id) === String(user.id)) {
        hasHu = true
        hasTing = true
        maxTimes = Math.max(action.times || 0, maxTimes)
      }

      // 点炮
      if (action.type === '胡' && !!action.targets.find(target => String(target.id) === String(user.id) )) {
        paoSum += 1
      }

      // 听牌 (没被查叫，targets里没有)
      // 1) 胡  => 自己胡了
      // 2）查叫，没被查  =>
      //    》胡了；
      //    》查叫，自己发起查叫
      // 3) 没有查叫 => 三家胡了；打完牌各自有叫

      // 自己发起查叫
      if (!hasTing && action.type === '查叫' && String(action.owner.id) === String(user.id)) {
        hasTing = true
      }

      // 查叫, owner不是自己
      if (!hasTing && action.type === '查叫' && String(action.owner.id) !== String(user.id)) {
        beichajiaoAction = action
      }
    })

    if (!hasTing) {
      // 没有被查叫
      if (!beichajiaoAction) {
        hasTing = true
      } else if (!beichajiaoAction.targets.find(target => String(target.id) === String(user.id))){
        // 被查叫，但不包括自己
        hasTing = true
      }
    }

    huSum += hasHu ? 1 : 0
    tingSum += hasTing ? 1 : 0
  })

  return { huSum, paoSum, tingSum, maxTimes }
}

/**
 * Format timestamp to day, hour, minute, second
 * @param  {number} timestamp, unit milliseconds
 * @return {string}
 */
function formatDuration(timestamp) {
  const seconds = timestamp / 1000 | 0
  const minutes = seconds / 60 | 0
  const hours = minutes / 60 | 0
  const days = hours / 24 | 0

  const daysFormat = days ? days + '天' : ''
  const hoursFormat = hours % 24 ? hours % 24 + '小时' : ''
  const minutesFormat = minutes % 60 ? minutes % 60 + '分' : ''
  const secondFormat = seconds % 60 ? seconds % 60 + '秒' : ''

  return [
    daysFormat,
    hoursFormat,
    minutesFormat,
    secondFormat
  ].join('')
}
